#include <stdlib.h>
#include <string.h>

#include "lib.h"
#include "blockeddatabuffer.h"
using namespace Library;


struct BlockedDataBuffer::Block
{
	uint32		m_begin;
	uint32		m_end;
	Block *		m_next;

	// Do not place any member variables after this line.
	uint8		m_data[1];
};

//----------------------------------------------------------------------------
// Public
//
BlockedDataBuffer::BlockedDataBuffer(uint32 blockSize)
{
	m_blockSize	= blockSize;
	m_head		= 0;
	m_tail		= 0;
}

BlockedDataBuffer::~BlockedDataBuffer()
{
	while (removeBlock())
		/* nothing */;
}

bool8
BlockedDataBuffer::add(uint8 * data, uint32 length)
{
	if (!data)
		return false;

	if (length == 0)
		return true;

	copyData(&data, &length, m_tail);
	while (length)
	{
		if (!addBlock())
			return false;

		copyData(&data, &length, m_tail);
	}

	return true;
}

bool8
BlockedDataBuffer::get(uint8 ** data, uint32 * length)
{
	// No place to put the data.
	if (!data || !length)
		return false;

	// No data available.
	if (!m_head || m_head->m_begin == m_head->m_end)
		return false;

	*data	= m_head->m_data + m_head->m_begin;
	*length	= m_head->m_end - m_head->m_begin;

	return true;
}

bool8
BlockedDataBuffer::remove(uint32 length)
{
	if (!m_head)
		return false;

	m_head->m_begin += length;
	if (m_head->m_begin > m_head->m_end)
		m_head->m_begin = m_head->m_end;

	if (m_head->m_begin == m_blockSize)
		removeBlock();

	return true;
}

//----------------------------------------------------------------------------
// Protected
//
bool8
BlockedDataBuffer::addBlock()
{
	Block *		block;

	block = (Block *)new uint8[ sizeof Block + m_blockSize ];
	if (!block)
		return false;

	// Initialize only the accounting information...not the data container.
	memset(block, 0, sizeof Block);

	if (m_tail)
	{
		// Non-empty list.  Append to the end.
		m_tail->m_next = block;
		m_tail = block;
	}
	else
	{
		// Empty list.  Initialize the variables.
		m_head = block;
		m_tail = block;
	}

	return true;
}

bool8
BlockedDataBuffer::removeBlock()
{
	Block *		block;

	// No blocks available for removal.
	if (!m_head)
		return false;

	block	= m_head;
	m_head	= m_head->m_next;

	// Check to see if we removed the last block.
	if (!m_head)
		m_tail = 0;

	delete [] (uint8*)block;

	return true;
}

bool8
BlockedDataBuffer::copyData(uint8 ** data, uint32 * length, Block * block)
{
	uint8 *	dst;
	uint32	dstlen;
	uint32	cpylen;

	if (!block)
		return false;

	dst		= block->m_data + block->m_end;
	dstlen	= m_blockSize - block->m_end;
	cpylen	= MIN(dstlen, *length);

	memcpy(dst, *data, cpylen);

	block->m_end += cpylen;
	*data        += cpylen;
	*length      -= cpylen;

	return true;
}
